﻿using System;

namespace Alfa.Math {
	public class Fraction : IEquatable<Fraction> {
		int num, den;
		public Fraction(int num) {
			this.num = num;
			this.den = 1;
		}
		public Fraction(int num, int den) {
			this.num = num;
			this.den = den;
		}

		// user-defined conversion from Fraction to double 
		public static implicit operator double(Fraction f) {
			return (double)f.num / f.den;
		}

		public bool Equals(Fraction other) {
			return this.num.Equals(other.num) && this.den.Equals(other.den);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction Add(Fraction value1, Fraction value2) {
			return new Fraction(value1.num * value2.den + value2.num * value1.den, value1.den * value2.den);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] Sum of the source vectors.</param>
		public static void Add(ref Fraction value1, ref Fraction value2, out Fraction result) {
			result = Add(value1, value2);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction Add(Fraction value1, int value2) {
			return new Fraction(value1.num + value2 * value1.den, value1.den);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] Sum of the source vectors.</param>
		public static void Add(ref Fraction value1, ref int value2, out Fraction result) {
			result = Add(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <returns></returns>
		public static Fraction Divide(Fraction value1, Fraction value2) {
			return new Fraction(value1.num * value2.den, value1.den * value2.num);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <param name="result">[OutAttribute] The result of the division.</param>
		public static void Divide(ref Fraction value1, Fraction value2, out Fraction result) {
			result = Divide(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <returns></returns>
		public static Fraction Divide(Fraction value1, int value2) {
			return new Fraction(value1.num, value1.den * value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <param name="result">[OutAttribute] The result of the division.</param>
		public static void Divide(ref Fraction value1, int value2, out Fraction result) {
			result = Divide(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <returns></returns>
		public static Fraction Divide(int value1, Fraction value2) {
			return new Fraction(value1 * value2.den, value2.num);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">The divisor.</param>
		/// <param name="result">[OutAttribute] The result of the division.</param>
		public static void Divide(ref int value1, Fraction value2, out Fraction result) {
			result = Divide(value1, value2);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <returns></returns>
		public static Fraction Negate(Fraction value) {
			return new Fraction(-value.num, value.den);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="result">[OutAttribute] Vector pointing in the opposite direction.</param>
		public static void Negate(ref Fraction value, out Fraction result) {
			result = Negate(value);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Fraction Multiply(Fraction value1, Fraction value2) {
			return new Fraction(value1.num * value2.num, value1.den * value2.den);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <param name="result">[OutAttribute] The result of the multiplication.</param>
		public static void Multiply(ref Fraction value1, Fraction value2, out Fraction result) {
			result = Multiply(value1, value2);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Fraction Multiply(Fraction value1, int value2) {
			return new Fraction(value1.num * value2, value1.den);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <param name="result">[OutAttribute] The result of the multiplication.</param>
		public static void Multiply(ref Fraction value1, int value2, out Fraction result) {
			result = Multiply(value1, value2);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction Subtract(Fraction value1, Fraction value2) {
			return new Fraction(value1.num * value2.den - value2.num * value1.den, value1.den * value2.den);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] The result of the subtraction.</param>
		public static void Subtract(ref Fraction value1, ref Fraction value2, out Fraction result) {
			result = Subtract(value1, value2);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction Subtract(Fraction value1, int value2) {
			return new Fraction(value1.num - value2 * value1.den, value1.den);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] The result of the subtraction.</param>
		public static void Subtract(ref Fraction value1, ref int value2, out Fraction result) {
			result = Subtract(value1, value2);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction Subtract(int value1, Fraction value2) {
			return new Fraction(value1 * value2.den - value2.num, value2.den);
		}

		/// <summary>
		/// Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <param name="result">[OutAttribute] The result of the subtraction.</param>
		public static void Subtract(ref int value1, ref Fraction value2, out Fraction result) {
			result = Subtract(value1, value2);
		}

		/// <summary>
		/// Returns a vector pointing in the opposite direction.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator -(Fraction value) {
			return Negate(value);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator +(Fraction value1, Fraction value2) {
			return Add(value1, value2);
		}

		/// <summary>
		///  Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator -(Fraction value1, Fraction value2) {
			return Subtract(value1, value2);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Fraction operator *(Fraction value1, Fraction value2) {
			return Multiply(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="divider">The divisor.</param>
		/// <returns></returns>
		public static Fraction operator /(Fraction value1, Fraction value2) {
			return Divide(value1, value2);
		}

		/// <summary>
		/// Tests vectors for equality.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static bool operator ==(Fraction value1, Fraction value2) {
			return value1.Equals(value2);
		}

		/// <summary>
		/// Tests vectors for inequality.
		/// </summary>
		/// <param name="value1">Vector to compare.</param>
		/// <param name="value2">Vector to compare.</param>
		/// <returns></returns>
		public static bool operator !=(Fraction value1, Fraction value2) {
			return !value1.Equals(value2);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator +(Fraction value1, int value2) {
			return Add(value1, value2);
		}

		/// <summary>
		/// Adds two vectors.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator +(int value1, Fraction value2) {
			return Add(value2, value1);
		}

		/// <summary>
		///  Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator -(Fraction value1, int value2) {
			return Subtract(value1, value2);
		}

		/// <summary>
		///  Subtracts a vector from a vector.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static Fraction operator -(int value1, Fraction value2) {
			return Subtract(value1, value2);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Fraction operator *(Fraction value1, int value2) {
			return Multiply(value1, value2);
		}

		/// <summary>
		/// Multiplies a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="scaleFactor">Scalar value.</param>
		/// <returns></returns>
		public static Fraction operator *(int value1, Fraction value2) {
			return Multiply(value2, value1);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="divider">The divisor.</param>
		/// <returns></returns>
		public static Fraction operator /(Fraction value1, int value2) {
			return Divide(value1, value2);
		}

		/// <summary>
		/// Divides a vector by a scalar value.
		/// </summary>
		/// <param name="value">Source vector.</param>
		/// <param name="divider">The divisor.</param>
		/// <returns></returns>
		public static Fraction operator /(int value1, Fraction value2) {
			return Divide(value1, value2);
		}

		/// <summary>
		/// Tests vectors for equality.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static bool operator ==(Fraction value1, int value2) {
			return value1.Equals(value2);
		}

		/// <summary>
		/// Tests vectors for equality.
		/// </summary>
		/// <param name="value1">Source vector.</param>
		/// <param name="value2">Source vector.</param>
		/// <returns></returns>
		public static bool operator ==(int value1, Fraction value2) {
			return value1.Equals(value2);
		}

		/// <summary>
		/// Tests vectors for inequality.
		/// </summary>
		/// <param name="value1">Vector to compare.</param>
		/// <param name="value2">Vector to compare.</param>
		/// <returns></returns>
		public static bool operator !=(Fraction value1, int value2) {
			return !value1.Equals(value2);
		}

		/// <summary>
		/// Tests vectors for inequality.
		/// </summary>
		/// <param name="value1">Vector to compare.</param>
		/// <param name="value2">Vector to compare.</param>
		/// <returns></returns>
		public static bool operator !=(int value1, Fraction value2) {
			return !value1.Equals(value2);
		}

		public override bool Equals(object obj) {
			return obj.GetType( ).Equals(typeof(Fraction)) && Equals((Fraction)obj);
		}

		public override int GetHashCode( ) {
			return base.GetHashCode( );
		}

		public override string ToString( ) {
			return this.num.ToString( ) + "/" + this.den.ToString( );
		}
	}
}
